package com.owlab.dcs;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.math.BigDecimal;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;

public class ComServer implements Runnable{
	private Logger logger = null;
	private Socket socket;

	private static final BigDecimal FOUR = BigDecimal.valueOf(4);
	private static final int roundingMode = BigDecimal.ROUND_HALF_EVEN;
	private static int numOfDigits = -1;

	private String serverLocation = null;
	private int serverPort = -1;

	//	Worker(Socket socket){
	//		logger = Logger.getLogger(this.getClass().getName());
	//		logger.log(Level.INFO, "A worker started");
	//		logger.log(Level.INFO, "Digits of PI="+ numOfDigits);
	//		
	//		this.socket = socket;
	//	}

	ComServer(Socket socket, int numOfDigits, String serverLocation, int serverPort){
		logger = Logger.getLogger(this.getClass().getName());

		this.socket = socket;
		this.numOfDigits = numOfDigits;
		this.serverLocation = serverLocation;
		this.serverPort = serverPort;

		logger.log(Level.INFO, "A worker created: numOfDigits="+numOfDigits +" nextServer="+serverLocation+" nextPort="+serverPort);
	}
	@Override
	public void run() {
		String request = null;
		String response = null;
		// TODO Auto-generated method stub
		BufferedReader in = null;
		//Reader in = null;
		PrintWriter out = null;
		//Writer out = null;
		try {
			in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			out = new PrintWriter(socket.getOutputStream(),true);

			// maybe "s"
			request = in.readLine();

			// If this is not the tail, do something before next call
			computePi(numOfDigits);
			// make a call to next component
			Client client = new Client(serverLocation, serverPort);
			response = client.makeCall(request);


			// do something after call completion, if you want
			out.println(response);

			// closing
			out.close();
			in.close();
			socket.close();
		}catch(IOException e){
			System.out.println(e);
			System.exit(-1);
		}
		//}
	}
	private static BigDecimal computePi(int digits){
		//BigDecimal FOUR = BigDecimal.valueOf(4);
		int scale = digits + 5;
		BigDecimal arctan1_5 = arctan(5, scale);
		BigDecimal arctan1_239 = arctan(239, scale);
		BigDecimal pi = arctan1_5.multiply(FOUR).subtract(arctan1_239).multiply(FOUR);
		return pi.setScale(digits, BigDecimal.ROUND_HALF_UP);

	}
	/**
	 * Compute the value, in radians, of the arctangent of 
	 * the inverse of the supplied integer to the specified
	 * number of digits after the decimal point.  The value
	 * is computed using the power series expansion for the
	 * arc tangent:
	 *
	 * arctan(x) = x - (x^3)/3 + (x^5)/5 - (x^7)/7 + 
	 *     (x^9)/9 ...
	 */   
	public static BigDecimal arctan(int inverseX, 
			int scale) 
	{
		BigDecimal result, numer, term;
		BigDecimal invX = BigDecimal.valueOf(inverseX);
		BigDecimal invX2 = 
			BigDecimal.valueOf(inverseX * inverseX);

		numer = BigDecimal.ONE.divide(invX,
				scale, roundingMode);

		result = numer;
		int i = 1;
		do {
			numer = 
				numer.divide(invX2, scale, roundingMode);
			int denom = 2 * i + 1;
			term = 
				numer.divide(BigDecimal.valueOf(denom),
						scale, roundingMode);
			if ((i % 2) != 0) {
				result = result.subtract(term);
			} else {
				result = result.add(term);
			}
			i++;
		} while (term.compareTo(BigDecimal.ZERO) != 0);
		return result;
	}



}
